#ifndef __COMMON_FUNCTIONS_H__
#define __COMMON_FUNCTIONS_H__

#include "Common/World/Angle.h"
#include "Common/World/Vector.h"
#include "Common/World/Point.h"
#include "Common/World/ParamStructs.h"
#include "Common/Support/Debug.h"



//DEBUGOVACIE MAKRA
//#define CATCH_DBGN DBGN
#define CATCH_DBGN DO_NOTHING

//#define SHOUT_DBGN DBGN
#define SHOUT_DBGN DO_NOTHING

//#define SKILLS_DBGN DBGN
#define SKILLS_DBGN DO_NOTHING

//#define UBLK_DBGN DBGN
#define UBLK_DBGN DO_NOTHING

#ifdef DEBUG
#define G_TXTN	TXTN
#define G_TXT	TXT
#else
#define G_TXTN	DO_NOTHING
#define G_TXT	DO_NOTHING
#endif //DEBUG

#ifdef WIN32
#ifndef min
#define min(x,y) ( ((x) > (y)) ? (y) : (x) )
#endif
#endif

#ifndef sqr
#define sqr(x) (x)*(x)
#endif


/**
 *   Tu by mohli byt hodene globalne funkcie, ktore su teraz napr. definovane v PlayerSkills
 *   a triedy, funkcie na vypocet uhlov, vektorov atd. Zatial iba nakopirovane globalne
 *   funkcie z PlayerSkills
 */

// Vrati uhol vstupneho vektora zmenseni o uhol zadany na vstupe.
static Angle CalcDir(const Vector& vect, Angle rotation)
{
	Vector tmp = vect - rotation;
	return tmp.GetAngle();
}

// Prevod desatineho cisla na cele
// Priklad zaokruhleneho vstupu: (0.49 -> 0, 0.5 -> 1)
inline static int f2int(float fX)
{ 
	return int(fX + 0.5f);
}


// Prelinaju sa ciary?
inline static bool DoLinesIntersect(Point A, Vector U, Point B, Vector V)
{
	// Do lines (A + t1 * U) and (B + t2 * V) intersect?
	float t1 = (U.GetX() * B.GetY() - U.GetX() * A.GetY() - U.GetY() * B.GetX() + U.GetY() * A.GetX()) / (U.GetY() * V.GetX() - U.GetX() * V.GetY());
	float t2 = (V.GetX() * A.GetY() - V.GetX() * B.GetY() - V.GetY() * A.GetX() + V.GetY() * B.GetX()) / (V.GetY() * U.GetX() - V.GetX() * U.GetY());

	return t1 >= 0.f && t1 <= 1.f && t2 >= 0.f && t2 <= 1.f;
}


// Vrati, ci ma alebo nema priamka priesecnik s kruznicou. Vypocitany priesecnik je ulozeny do T1.
// POZN.: Tato funkcia je divna. Priamka moze mat aj 2 priesecniky s kruznicou.
static int GetLineCircleIntersect(Point A, Point B, Point S, float r, Point &T1)
{
	static float Ax, Ay, Bx, By, Sx, Sy;
	static float a, b, c, t;
	Ax = A.GetX(); Bx = B.GetX(); Sx = S.GetX();
	Ay = A.GetY(); By = B.GetY(); Sy = S.GetY();
	a = 2 * (sqr(Ax-Bx) + sqr(Ay-By));
	
	if(fabsf(a) < .0001f)
		return 0;

	b = -4*(sqr(Ax-Bx) + sqr(Ay-By))*(sqr(Ax)-sqr(r)-2*Ax*Sx+sqr(Sx)+sqr(Ay-Sy));
	b += 4*sqr((sqr(Ax)+sqr(Ay)+Bx*Sx-Ax*(Bx+Sx)+By*Sy-Ay*(By+Sy)));
	
	if(b < 0)
		return 0;
	b = sqrtf(b);

	c = 2*(Ax-Bx)*(Ax-Sx)+2*(Ay-By)*(Ay-Sy);

	t = (c - b) / a;
	if(t > 0)
	{
		T1.SetX(Ax + (Bx-Ax)*t);
		T1.SetY(Ay + (By-Ay)*t);
		return 1;
	}
	t = (c + b) / a;
	if(t > 0)
	{
		T1.SetX(Ax + (Bx-Ax)*t);
		T1.SetY(Ay + (By-Ay)*t);
		return 1;
	}
	return 0;
}


////otocenie usecky tvorenej dvoma bodmi o urcity uhol, aspon myslim :)
static void RotatePoint(double lfPointCenterX, double lfPointCenterY, double& lfPoint1X, double& lfPoint1Y, double lfRadUhol)
{
	double lfUhol = atan2((lfPoint1X - lfPointCenterX), (lfPoint1Y - lfPointCenterY));
	double lfRadius = sqrt(pow((lfPoint1X - lfPointCenterX), 2) + pow((lfPoint1Y - lfPointCenterY),2));
	
	lfPoint1X = lfPointCenterX + cos(lfRadUhol + lfUhol) * lfRadius;
	lfPoint1Y = lfPointCenterY + sin(lfRadUhol + lfUhol) * lfRadius;	
}


// Vyriesi kvadraticku rovnicu
// a,b,c su koeficienty rovnice, x1, x2 su vysledky kvadratickej rovncie
// vrati true, ak x1 aj x2 su platne, inak false (determinant < 0)
inline static bool Quadrat(float a, float b, float c, float* x1, float* x2)
{
	const float d = b*b - 4*a*c;
	
	// diskriminant zaporny
	if(d < 0)
		return false;

	const float sq_d = sqrtf(d);

	*x1 = (-b + sq_d) / (2*a);
	*x2 = (-b - sq_d) / (2*a);
	
	return true;
}

// Zisti, ci bod patri do obdlznikovej oblasti
static bool IsPointInRectArea(double lfPointX, double lfPointY, double lfLeft, double lfTop, double lfRight, double lfBottom)
{
	if((lfLeft <= lfPointX) && (lfPointX <= lfRight) && (lfTop <= lfPointY) && (lfPointY <= lfBottom))
		return true;
	else
		return false;
}

// Je dany bod medzi bodmi p1 a p2?
static bool InTheMiddle(const Point& tested, const Point& p1, const Point& p2)
{
	const float distP1P2 = (p1 - p2).GetLength();

	if ((tested - p1).GetLength() > distP1P2)
		return false;
	if ((tested - p2).GetLength() > distP1P2)
		return false;

	return true;
}

//vrati poradia znaku v abecede...
static int GetUnit(char type)
{
	return type - 'A';
}

// Vrati pocet priesecnikov 2 kruznic (S1, r1) and (S2, r2). Ak take body existuju, tak su to body T1 a T2.
static int CirclesIntersection(const Point S1, const float r1, const Point S2, const float r2, Point& T1, Point& T2)
{
	#define EPS 1e-6

	static float S1x, S1y, S2x, S2y;
	static float a, b, c, d;

	S1x = S1.GetX(); S1y = S1.GetY();
	S2x = S2.GetX(); S2y = S2.GetY();

	if(fabsf(S1x-S2x) < EPS)
	{
		if(fabsf(S1y-S2y) < EPS)
			return 0;
		
		T1.SetY((sqr(r1) - sqr(r2) - sqr(S1y) + sqr(S2y)) / (2*(S2y-S1y)));
		T2.SetY(T1.GetY());
		a = -sqr(S1y-S2y) * (r1-r2+S1y-S2y) * (r1+r2+S1y-S2y) * (r1-r2-S1y+S2y) * (r1+r2-S1y+S2y);
		
		if(a < 0)
			return 0;
		
		a = sqrtf(a) / (2*sqr(S1y-S2y));
		T1.SetX(S2x - a);
		T2.SetX(S2x + a);
	} 
	else
	{
		a = sqr(S1x - S2x) + sqr(S1y - S2y);
		b = -sqr(S1x-S2x) * (-sqr(r1-r2)+a) * (-sqr(r1+r2)+a);
		if(b < 0)
			return 0;
		b = sqrtf(b);
		c = (S1x-S2x) * (sqr(r2)*(S1x-S2x) + sqr(r1)*(S2x-S1x) + (S1x+S2x)*a);
		T1.SetX((c + b*(S1y-S2y)) / (2*(S1x-S2x)*a));
		T2.SetX((c + b*(S2y-S1y)) / (2*(S1x-S2x)*a));
		d = sqr(r2)*(S1y-S2y) + sqr(r1)*(S2y-S1y) + a*(S1y+S2y);
		T1.SetY((-b + d) / (2*a));
		T2.SetY((b + d) / (2*a));
	}
	return 2;
}

// Vrati pocet dotycnicovych bodov kruznice S o polomere r a dotycnice prechadzajucej cez bod A.
// Ak take body existuju, tak su to body T1 a T2.
static int GetTangentPointsOnCircle(Point S, float r, Point A, Point& T1, Point& T2)
{
	static float p1, p2, p3, p4;
	static float Sx, Sy, Ax, Ay;
	Sx = S.GetX(); Sy = S.GetY();
	Ax = A.GetX(); Ay = A.GetY();
	
	if(fabs(Sx - Ax) < EPS && fabs(Sy - Ay) < EPS)
		return 0;
	p1 = sqr(Ax) - sqr(r) - 2*Ax*Sx + sqr(Sx) + sqr(Ay - Sy);
	
	if(p1 < 0)
		return 0;

	p1 = r * sqrtf(p1);
	p2 = sqr(Ax - Sx) + sqr(Ay - Sy);
	p3 = sqr(Ay)*Sy + Ay*(sqr(r)-2*sqr(Sy)) + Sy*(-sqr(r)+sqr(Ax-Sx)+sqr(Sy));
	p4 = sqr(Ax)*Sx + Ax*(sqr(r)-2*sqr(Sx)) + Sx*(-sqr(r)+sqr(Ay-Sy)+sqr(Sx));
	T1.SetY(-p1 * (Ax - Sx)); T1.SetX(p1 * (Ay - Sy));
	T2.SetY(-T1.GetY()); T2.SetX(-T1.GetX());
	T1.SetY((T1.GetY() + p3) / p2);
	T2.SetY((T2.GetY() + p3) / p2);
	T1.SetX((p4 + T1.GetX()) / p2);
	T2.SetX((p4 + T2.GetX()) / p2);

	#undef EPS

	return 2;
}

#endif
